package tools

import (
	"reflect"
	"hub.hddznet.com/app/framework/errs"
)

func RftStruct(obj interface{}, fn func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag) error) error {
	ty := reflect.TypeOf(obj)
	vl := reflect.ValueOf(obj)
	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
		vl = vl.Elem()
	}
	if ty.Kind() == reflect.Struct {
		for i := 0; i < ty.NumField(); i++ {
			t := ty.Field(i).Type
			v := vl.Field(i)
			tag := ty.Field(i).Tag
			err := fn(t, v, tag)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// 解析struct对象
func RftStructDeep(obj interface{}, fn func(t1 reflect.Type, v1 reflect.Value, tg reflect.StructTag)) {
	if fn == nil {
		panic("函数不能为空")
	}
	ty := reflect.TypeOf(obj)
	vl := reflect.ValueOf(obj)

	if ty.Kind() == reflect.Ptr {
		if vl.Pointer() == 0 {
			panic("第一个参数obj不能为nil")
		}
	}
	rftSelfDeep(ty, vl, "", fn)
}

func rftSelfDeep(ty reflect.Type, vl reflect.Value, tg reflect.StructTag, fn func(rty reflect.Type, rvl reflect.Value, rtg reflect.StructTag)) {
	//传入的内容为struct类型
	switch RftRealKind(ty) {
	case reflect.Struct:
		if ty.Kind() == reflect.Ptr {
			if vl.Pointer() == 0 {
				fn(ty, vl, tg)
				return
			}
			ty = ty.Elem()
			vl = vl.Elem()
		}

		fn(ty, vl, tg)
		for i := 0; i < ty.NumField(); i++ {
			t := ty.Field(i).Type
			tag := ty.Field(i).Tag
			v := vl.Field(i)
			rftSelfDeep(t, v, tag, fn)
		}
	case reflect.String:
		fn(ty, vl, tg)
	case reflect.Bool:
		fn(ty, vl, tg)
	case reflect.Int:
		fn(ty, vl, tg)
	case reflect.Int8:
		fn(ty, vl, tg)
	case reflect.Int16:
		fn(ty, vl, tg)
	case reflect.Int32:
		fn(ty, vl, tg)
	case reflect.Int64:
		fn(ty, vl, tg)
	case reflect.Uint:
		fn(ty, vl, tg)
	case reflect.Uint8:
		fn(ty, vl, tg)
	case reflect.Uint16:
		fn(ty, vl, tg)
	case reflect.Uint32:
		fn(ty, vl, tg)
	case reflect.Uint64:
		fn(ty, vl, tg)
	case reflect.Float32:
		fn(ty, vl, tg)
	case reflect.Float64:
		fn(ty, vl, tg)
	case reflect.Func:
		fn(ty, vl, tg)

	}
}

//只支持单层struct反射，也就是说，struct中不能再包含struct属性
func RftModelToView(view interface{}, model interface{}) {
	vty := reflect.TypeOf(view)
	vvl := reflect.ValueOf(view)
	if vty.Kind() != reflect.Ptr {
		panic("view 与 model 的转换，view必须为指针类型")
	}
	vty = vty.Elem()
	vvl = vvl.Elem()
	switch vty.Kind() {
	case reflect.Struct:
		RftStructView(view, model)
	case reflect.Slice:
		RftSliceView(view, model)
	default:
		panic("view 必须为struct或者slice")

	}
}

func RftStructView(view interface{}, model interface{}) {
	vty := reflect.TypeOf(view).Elem()
	vvl := reflect.ValueOf(view).Elem()

	mty := reflect.TypeOf(model)
	mvl := reflect.ValueOf(model)
	if mty.Kind() == reflect.Ptr {
		mty = mty.Elem()
		mvl = mvl.Elem()
	}

	for i := 0; i < vty.NumField(); i++ {
		vt := vty.Field(i).Type
		vv := vvl.Field(i)
		fieldname := vty.Field(i).Name
		mv := mvl.FieldByName(fieldname)
		mts, ok := mty.FieldByName(fieldname)
		if !ok {
			continue
		}
		mt := mts.Type
		if vt.Kind() == mt.Kind() && vv.CanSet() {
			vv.Set(mv)
		} else if (vt.Kind() == reflect.Ptr) && (vt.Elem().Kind() == mt.Kind() && vv.Elem().CanSet()) {
			vv.Elem().Set(mv)
		} else if (mt.Kind() == reflect.Ptr && mv.Pointer() != 0) && (vt.Kind() == mt.Elem().Kind() && vv.CanSet()) {
			vv.Set(mv.Elem())
		}
	}
}

func RftSliceView(view interface{}, model interface{}) {

}

func RftRealKind(ty reflect.Type) reflect.Kind {
	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
		RftRealKind(ty)
	}
	return ty.Kind()
}
func RftRealType(obj interface{}) reflect.Type {
	ty := reflect.TypeOf(obj)
	return RftRealType2(ty)
}

func RftRealType2(ty reflect.Type) reflect.Type {
	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
		return RftRealType2(ty)
	}
	return ty
}

func If(condition bool, trueVal, falseVal interface{}) interface{} {
	if condition {
		return trueVal
	}
	return falseVal
}

func Contains(obj interface{}, target interface{}) (bool, error) {
	targetValue := reflect.ValueOf(target)
	switch reflect.TypeOf(target).Kind() {
	case reflect.Slice, reflect.Array:
		for i := 0; i < targetValue.Len(); i++ {
			if targetValue.Index(i).Interface() == obj {
				return true, nil
			}
		}
	case reflect.Map:
		if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
			return true, nil
		}
	}
	return false, errs.ParamNotInPtrErr("not in")
}
